package cn.qingyun.gis.modules.school.service.impl;

import cn.hutool.core.annotation.Alias;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.text.csv.CsvWriter;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.qingyun.gis.exception.CommonException;
import cn.qingyun.gis.modules.express.config.Exception.CustomExceptionHandler;
import cn.qingyun.gis.modules.express.constant.ExpressStatus;
import cn.qingyun.gis.modules.express.constant.RedisContant;
import cn.qingyun.gis.modules.express.entity.ExpressRegion;
import cn.qingyun.gis.modules.express.entity.OperationLog;
import cn.qingyun.gis.modules.express.mapper.ExpressRegionMapper;
import cn.qingyun.gis.modules.express.mapper.OperationLogMapper;
import cn.qingyun.gis.modules.express.service.IExpressRegionService;
import cn.qingyun.gis.modules.express.util.SyncTool;
import cn.qingyun.gis.modules.init.system.constant.enums.RegionNumPermissionEnum;
import cn.qingyun.gis.modules.init.system.entity.SftpConfig;
import cn.qingyun.gis.modules.init.system.entity.SysLog;
import cn.qingyun.gis.modules.init.system.entity.SysUser;
import cn.qingyun.gis.modules.init.system.mapper.SysLogMapper;
import cn.qingyun.gis.modules.init.system.mapper.SysUserMapper;
import cn.qingyun.gis.modules.init.utils.CsvTool;
import cn.qingyun.gis.modules.init.utils.DictUtil;
import cn.qingyun.gis.modules.init.utils.JwtUtil;
import cn.qingyun.gis.modules.init.utils.SftpUtils;
import cn.qingyun.gis.modules.school.entity.DistinctInfo;
import cn.qingyun.gis.modules.school.entity.K12SchoolsInfo;
import cn.qingyun.gis.modules.school.entity.K12SchoolsProofread;
import cn.qingyun.gis.modules.school.entity.dto.*;
import cn.qingyun.gis.modules.school.entity.request.K12SchoolProofreadQueryRequest;
import cn.qingyun.gis.modules.school.entity.request.K12SchoolsProofreadRequest;
import cn.qingyun.gis.modules.school.mapper.K12SchoolsInfoMapper;
import cn.qingyun.gis.modules.school.mapper.K12SchoolsProofreadMapper;
import cn.qingyun.gis.modules.school.service.IK12SchoolsProofreadService;
import cn.qingyun.gis.modules.youshang.entity.dto.ExportChannelRecordDto;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.csvreader.CsvReader;
import com.google.common.collect.Lists;
import com.jcraft.jsch.ChannelSftp;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.io.WKTReader;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>
 * K12学校勘误 服务实现类
 * </p>
 *
 * @author guest
 * @since 2024-01-04
 */
@Service
@Slf4j
public class K12SchoolsProofreadServiceImpl extends ServiceImpl<K12SchoolsProofreadMapper, K12SchoolsProofread> implements IK12SchoolsProofreadService {

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private CustomExceptionHandler customExceptionHandler;
    @Resource
    private IExpressRegionService iExpressRegionService;

    @Resource
    private K12SchoolsInfoMapper k12SchoolsInfoMapper;
    @Resource
    private K12SchoolsProofreadMapper k12SchoolsProofreadMapper;
    @Resource
    private SysLogMapper sysLogMapper;

    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private ExpressRegionMapper expressRegionMapper;
    @Resource
    private HttpServletResponse response;

    @Resource
    private OperationLogMapper operationLogMapper;

    @Value("${qingyun.sftp.cachePath:/tmp/}")
    private String cachePath; // 本地缓存路径

    @Override
    @Transactional
    public String proofread(K12SchoolsProofreadRequest request) {
        log.debug("proofread begin：{}", JSONUtil.toJsonStr(request));
        RLock rlock = redissonClient.getLock(RedisContant.K12_SCHOOL_PROOF_LOCK);
        try {
            boolean lockRes = rlock.tryLock(10, TimeUnit.SECONDS);
            if (lockRes) {
                //处理k12_schools_proofread表
                K12SchoolsProofread k12SchoolsProofread = k12SchoolsProofreadMapper.selectById(request.getId());
                k12SchoolsProofread.setKwStatus(1); //更新勘误状态

                if (request.getGridBelongStatus() == 0) { // 优化：若勘误过程，站点不属于本网格，选择应属网格，勘误状态更新为：未勘误
                    k12SchoolsProofread.setKwStatus(ExpressStatus.UN_PROOFED);
                    // 自动匹配至应归属网格、应归属区县  request.getBelongGrid()
                    k12SchoolsProofread.setGridName(request.getBelongGrid());
                    k12SchoolsProofread.setGridId(request.getBelongGridCode());
                    k12SchoolsProofread.setRegionDistricts(request.getBelongCounty());
                    k12SchoolsProofread.setRegionDistrictsCode(request.getBelongCountyCode());
                    // 进行日志记录入库
                    OperationLog operationLog = OperationLog.builder()
                            .logAccount(request.getKwPhone())
                            .operationId(request.getId())
                            .logType(0) // 0:应属网格操作
                            .operateType(4)  //4:学校
                            .createTime(DateUtil.date()).build();
                    operationLogMapper.insert(operationLog);
                }

                k12SchoolsProofread.setKwPhone(request.getKwPhone());
                k12SchoolsProofread.setUpdateTime(DateUtil.date());//更新修改(勘误)时间
                k12SchoolsProofreadMapper.updateById(k12SchoolsProofread);

                //处理k12_schools_info 表
                K12SchoolsInfo k12SchoolsInfo =
                        k12SchoolsInfoMapper.selectOne(new LambdaQueryWrapper<K12SchoolsInfo>()
                                .eq(K12SchoolsInfo::getK12SchoolsProofreadId, request.getId()));
                K12SchoolsInfo info = new K12SchoolsInfo();
                if (ObjectUtil.isEmpty(k12SchoolsInfo)) { //该学校地址还未勘误
                    BeanUtil.copyProperties(request, info);
                    info.setId(null);
                    info.setK12SchoolsProofreadId(request.getId());
                    info.setCreateTime(DateUtil.date()); // 初次勘误时间记录
                    info.setUpdateTime(DateUtil.date());
                    k12SchoolsInfoMapper.insert(info);
                } else {//学校地址已勘误一次
                    BeanUtil.copyProperties(request, info);
                    info.setId(k12SchoolsInfo.getId());
                    info.setK12SchoolsProofreadId(request.getId());
                    info.setUpdateTime(DateUtil.date()); //勘误修改时间记录
                    k12SchoolsInfoMapper.updateById(info);
                }
                log.debug("proofread end");
                return "勘误成功";
            }
            log.debug("proofread end get no lock");
            return "系统繁忙，请稍后再试";
        } catch (Exception e) {
            throw new CommonException("勘误失败");
        } finally {
            rlock.unlock(); // 释放redis锁
        }
    }

    @Override
    public IPage<K12SchoolsProofread> queryList(K12SchoolProofreadQueryRequest request) {
        log.debug("K12SchoolsProofreadServiceImpl queryList begin");
        if (CharSequenceUtil.isNotEmpty(request.getGridType()) &&
                Boolean.TRUE.equals(RegionNumPermissionEnum.pitchRegion(request.getGridType()))) {
            // 创建分页对象
            IPage<K12SchoolsProofread> page = new Page<>(request.getPageNum(), request.getPageSize());
            //根据网格编码+工号+未勘误（勘误状态=0）+校对类型+修改时间降序匹配查询
            QueryWrapper<K12SchoolsProofread> queryWrapper = new QueryWrapper<>();
            if (ObjectUtil.isNotEmpty(request.getLat()) && ObjectUtil.isNotEmpty(request.getLon())) {
                queryWrapper.select("k12_schools_proofread.*, " +
                                String.format("round(ST_Distance_Sphere(POINT(%s, %s), POINT(lon, lat))/1000,2) AS distance", request.getLon(), request.getLat()))
                        .orderByAsc("distance");
            }
            queryWrapper.lambda().eq(K12SchoolsProofread::getKwStatus, 0).orderByDesc(K12SchoolsProofread::getUpdateTime);
            if (ObjectUtil.isNotEmpty(request.getKeyword())) {
                queryWrapper.lambda().and(
                        lwp -> lwp.like(K12SchoolsProofread::getPoiName, request.getKeyword()).
                                or().
                                like(K12SchoolsProofread::getMultiAddrChn, request.getKeyword())
                );
            }
            // 对前端网格列表进行处理
            String[] gridIdStr = request.getGridId().split(",");
            List<String> gridIdList = Arrays.asList(gridIdStr);
            // 工号为省级工号
            if (RegionNumPermissionEnum.PROVINCE.name().equals(request.getGridType()) && gridIdList.size() == 1) {
                log.debug("K12SchoolsProofreadServiceImpl queryList end");
                return k12SchoolsProofreadMapper.selectPage(page, queryWrapper);
            } else {
                queryWrapper.lambda().
                        and(
                                wp -> wp.in(K12SchoolsProofread::getRegionCityCode, gridIdList).
                                        or().
                                        in(K12SchoolsProofread::getRegionDistrictsCode, gridIdList).
                                        or().
                                        in(K12SchoolsProofread::getGridId, gridIdList)
                        );
                log.debug("K12SchoolsProofreadServiceImpl queryList end not province");
                return k12SchoolsProofreadMapper.selectPage(page, queryWrapper);
            }
        }
        return new Page<>();
    }

    @Override
    public IPage<K12SchoolInfoDTO> queryTotalList(K12SchoolProofreadQueryRequest request) {
        log.debug("K12SchoolsProofreadServiceImpl queryTotalList begin");
        //创建分页对象
        IPage<K12SchoolInfoDTO> page = new Page<>(request.getPageNum(), request.getPageSize());

        List<String> gridEncodings = Arrays.asList(request.getGridId().split(","));
        String gridType = request.getGridType();

        // 根据区域权限进行判定
        if (CharSequenceUtil.isNotEmpty(gridType) && RegionNumPermissionEnum.pitchRegion(request.getGridType())) {
            return page.setRecords(k12SchoolsProofreadMapper.selectSchoolTotalList(page, gridEncodings, gridType, request.getKeyword(), request.getLon(), request.getLat()));
        }
        log.debug("K12SchoolsProofreadServiceImpl queryTotalList end  gridType empty or has no permission");
        return new Page<>();
    }

    @Override
    public K12SchoolInfoDTO queryProofreadById(String id) {
        return k12SchoolsProofreadMapper.queryProofreadById(id);
    }

    @Override
    public void clearData() {
        this.remove(new QueryWrapper<>()); // 进行全表格式化
    }

    @Override
    @Transactional
    public void initData(MultipartFile file) {
        log.debug("initData begin");
        try {
            long startTime = System.currentTimeMillis(); //获取当前内容
            CsvReader csvReader = new CsvReader(file.getInputStream(), StandardCharsets.UTF_8);
            // 读表头
            csvReader.setSafetySwitch(false);
            csvReader.readHeaders();
            List<K12SchoolImportCsvData> imports = Lists.newArrayList();
            // 读内容
            while (csvReader.readRecord()) {
                imports.add(CsvTool.toBean(csvReader, K12SchoolImportCsvData.class));
            }
            int batchSize = 1000; //批次处理数据量
            if (CollUtil.isNotEmpty(imports)) {
                // 高并发导入
                int count = imports.size();
                int pages = (count / batchSize) + 1; // 1000条数据启动一个线程

                SyncTool.doSync(pages, i -> {
                    int batchNum = i == 1 ? 0 : (i - 1) * batchSize; //开始索引
                    int batchEnd = Math.min(batchNum + batchSize, count); //结束索引 = batchEnd - 1
                    List<K12SchoolsProofread> recordsBatch = Lists.newArrayList(); //存放批次数据
                    for (int j = batchNum; j < batchEnd; j++) {
                        K12SchoolImportCsvData anImport = imports.get(j);
                        K12SchoolsProofread k12SchoolsProofread = new K12SchoolsProofread();
                        BeanUtils.copyProperties(anImport, k12SchoolsProofread);
                        //csv中的poi_name 属性为 K12学校名称
                        k12SchoolsProofread.setProvince("山东");
                        k12SchoolsProofread.setPoiName(anImport.getPoiName());
                        k12SchoolsProofread.setLon(new BigDecimal(anImport.getLon()));
                        k12SchoolsProofread.setLat(new BigDecimal(anImport.getLat()));
                        k12SchoolsProofread.setKwStatus(0); //默认为未核实状态
                        // 导入时间数据
                        k12SchoolsProofread.setCreateTime(DateUtil.date());
                        k12SchoolsProofread.setUpdateTime(DateUtil.date());
                        recordsBatch.add(k12SchoolsProofread);
                    }
                    this.saveBatch(recordsBatch);
                    recordsBatch.clear();
                });
            }
            log.debug("数据解析成功，当前导入需要花费{}毫秒", System.currentTimeMillis() - startTime);
        } catch (Exception e) {
            log.error("数据解析失败", e);
        }

    }

    @Override
    @Transactional
    public void matchGrid(MultipartFile file) {

        Map<String, String> districtIdNameMap = getDistrictIdNameMap(file);

        AtomicInteger n = new AtomicInteger();
        // 获取微网格级别业务区划数据
        LambdaQueryWrapper<ExpressRegion> miroGridWrapper = new QueryWrapper<ExpressRegion>().lambda().eq(ExpressRegion::getLevel, "5").ne(ExpressRegion::getGeometry, "");
        List<ExpressRegion> miroGridTotal = iExpressRegionService.list(miroGridWrapper);
        // 获取网格级别业务区划数据
        LambdaQueryWrapper<ExpressRegion> gridWrapper = new QueryWrapper<ExpressRegion>().lambda().eq(ExpressRegion::getLevel, "4").ne(ExpressRegion::getGeometry, "");
        List<ExpressRegion> gridTotal = iExpressRegionService.list(gridWrapper);
        // 获取区县级别业务区划数据
        LambdaQueryWrapper<ExpressRegion> districtsWrapper = new QueryWrapper<ExpressRegion>().lambda().eq(ExpressRegion::getLevel, "3").ne(ExpressRegion::getGeometry, "");
        List<ExpressRegion> districtsTotal = iExpressRegionService.list(districtsWrapper);
        // 获取地市级别业务区划数据
        LambdaQueryWrapper<ExpressRegion> cityWrapper = new QueryWrapper<ExpressRegion>().lambda().eq(ExpressRegion::getLevel, "2").ne(ExpressRegion::getGeometry, "");
        List<ExpressRegion> cityTotal = iExpressRegionService.list(cityWrapper);

        // 计算全量友商信息数据条数
        int count = this.count();
        int pages = (count / 1000) + 1;
        long start = System.currentTimeMillis();

        // 根据数据总量划分数据块并启动多线程
        SyncTool.doSync(pages, i -> {
            Page<K12SchoolsProofread> page = new Page<>(i, 1000);
            List<K12SchoolsProofread> proofreads = this.page(page, null).getRecords();
            List<K12SchoolsProofread> updateRecords = Lists.newArrayList();

            for (K12SchoolsProofread data : proofreads) {
                data.setCity(districtIdNameMap.get(data.getCityCode()));
                data.setDistrict(districtIdNameMap.get(data.getDistrictCode()));

                try {
                    log.debug(String.format("====================开始匹配 %s====================", data.getId()));
                    // 如果站点经纬度都不为空或空字符串
                    if (ObjectUtil.isNotEmpty(data.getLon()) && ObjectUtil.isNotEmpty(data.getLat())) {
                        // 创建点对象
                        Point point = new GeometryFactory().createPoint(new Coordinate(data.getLon().doubleValue(), data.getLat().doubleValue()));
                        // 创建边界对象
                        Geometry geometry = null;
                        // 1.进行微网格匹配
                        for (ExpressRegion microGridData : miroGridTotal) {
                            // 创建面对象
                            geometry = new WKTReader().read(microGridData.getGeometry());
                            // 微网格匹配成功
                            if (geometry.contains(point)) {
                                data.setMicroGridId(microGridData.getCode());
                                data.setMicroGridName(microGridData.getName());
                                break;
                            }
                        }
                        // 2.进行网格匹配
                        for (ExpressRegion gridData : gridTotal) {
                            // 创建面对象
                            geometry = new WKTReader().read(gridData.getGeometry());
                            if (geometry.contains(point)) {
                                data.setGridId(gridData.getCode());
                                data.setGridName(gridData.getName());
                                break;
                            }
                        }
                        // 3.进行区县匹配
                        for (ExpressRegion disData : districtsTotal) {
                            geometry = new WKTReader().read(disData.getGeometry());
                            if (geometry.contains(point)) {
                                data.setRegionDistrictsCode(disData.getCode());
                                data.setRegionDistricts(disData.getName());
                                break;
                            }

                        }
                        // 4.进行地市匹配
                        for (ExpressRegion cityData : cityTotal) {
                            // 创建面对象
                            geometry = new WKTReader().read(cityData.getGeometry());
                            // 匹配地市
                            if (geometry.contains(point)) {
                                data.setRegionCityCode(cityData.getCode());
                                data.setRegionCity(cityData.getName());
                                break;
                            }
                        }
                    }
                    synchronized (this) {
                        n.incrementAndGet();
                        log.debug(String.format("=========== 当前匹配进度为%s / %s ===========", n, count));
                    }
                    updateRecords.add(data);
                } catch (Exception e) {
                    customExceptionHandler.handleException(String.format("友商站点-%s,入格失败！|报错：%s", data.getPoiName(), e.getMessage()), start);
                }
            }
            // 批量导入匹配之后数据
            this.updateBatchById(updateRecords);

        });
        log.debug(String.format("匹配网格完成，====一共处理%s学校数据====", count));
        // 加入日志
        SysLog sysLog = new SysLog();
        long finish = System.currentTimeMillis();
        sysLog.setLogContent("K12学校匹配数据结束");
        sysLog.setCreateTime(DateUtil.date());
        sysLog.setLogType(3);
        sysLog.setCostTime(finish - start);
        sysLog.setCreateBy("system");
        sysLog.setUserid("匹配任务");
        sysLogMapper.insert(sysLog);
    }

    @Override
    @SneakyThrows
    public void exportK12Summary(HttpServletRequest request) {
        log.debug("K12SchoolsInfoServiceImpl exportK12Summary begin");
        String fileName = "K12学校摸排详情汇总";
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("content-disposition", String.format("attachment;filename=%s.xlsx", new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + DateUtil.now()));

        //1. 根据工号进行汇总
        String currentUserName = JwtUtil.getUserNameByToken(request);
        // 根据用户信息查询区域编码
        SysUser user = sysUserMapper.selectOne(new QueryWrapper<SysUser>().lambda().select(SysUser::getRegionCode).
                eq(SysUser::getUsername, currentUserName));
        // 根据区域编码判断当前工号区域权限
        QueryWrapper<ExpressRegion> regionQueryWrapper = new QueryWrapper<>();
        log.debug("当前登录用户信息：{} 以及相应配置权限编码{}", currentUserName, user.getRegionCode());
        regionQueryWrapper.lambda().select(ExpressRegion::getLevel, ExpressRegion::getCode).eq(ExpressRegion::getCode, user.getRegionCode());
        ExpressRegion res = expressRegionMapper.selectOne(regionQueryWrapper);
        log.debug("ExpressRegion：{}", JSONUtil.toJsonStr(res));

        List<K12SchoolsProofreadExportDTO> k12ExportData = Lists.newArrayList();
        try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build()) {
            // 当前省级配置工号
            if ("0000SD".equals(user.getRegionCode())) {
                k12ExportData = k12SchoolsProofreadMapper.selectSummary("1", null);
            } else if (ObjectUtil.isNotEmpty(res.getLevel()) && ObjectUtil.isNotEmpty(res.getCode())) {
                k12ExportData = k12SchoolsProofreadMapper.selectSummary(res.getLevel(), res.getCode());
            }
            WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, "K12学校摸排详情汇总数据").head(ExportChannelRecordDto.class).build();
            excelWriter.write(k12ExportData, writeSheet);

            QueryWrapper<K12SchoolsProofread> k12SchoolsProofreadQueryWrapper = new QueryWrapper<>();
            // 并非省级工号
            if (!"0000SD".equals(user.getRegionCode())) {
                if ("3".equals(res.getLevel())) {
                    k12SchoolsProofreadQueryWrapper.lambda().eq(K12SchoolsProofread::getRegionDistrictsCode, res.getCode());
                }
                if ("2".equals(res.getLevel())) {
                    k12SchoolsProofreadQueryWrapper.lambda().eq(K12SchoolsProofread::getRegionCityCode, res.getCode());
                }
                if ("4".equals(res.getLevel())) {
                    k12SchoolsProofreadQueryWrapper.lambda().eq(K12SchoolsProofread::getGridId, res.getCode());
                }
            }
            int batchSize = 2000;
            int count = this.count(k12SchoolsProofreadQueryWrapper);
            int pages = (count / batchSize) + 1;
            String level;
            String code;
            if (ObjectUtil.isNotEmpty(res) && ObjectUtil.isNotEmpty(res.getLevel()) && ObjectUtil.isNotEmpty(res.getCode())) {
                level = res.getLevel();
                code = res.getCode();
            } else {
                code = "";
                level = "";
            }
            log.debug("level is ：{},code is :{}", level, code);
            WriteSheet detailSheet = EasyExcelFactory.writerSheet(1, "K12学校摸排全量明细数据").head(K12SchoolsProofreadExportTotalDTO.class).build();
            SyncTool.doSync(pages, i -> {
                Page<K12SchoolsProofreadExportTotalDTO> page = new Page<>(i, batchSize);
                List<K12SchoolsProofreadExportTotalDTO> exportYsChannelDtoList = k12SchoolsProofreadMapper.getProofreadSummaryData(page, level, code);
                synchronized (excelWriter) {
                    excelWriter.write(exportYsChannelDtoList, detailSheet);
                }
            });
        }
    }

    @Override
    public void uploadK12SummerData() {
        log.debug("K12SchoolsProofreadServiceImpl uploadK12SummerData begin");
        // 初始化Sftp 配置
        SftpConfig config = initSftpConfig();
        if (config == null) {
            log.debug("K12SchoolsProofreadServiceImpl uploadK12SummerData config error");
            return;
        }

        String sftpFileName = "K12学校摸排详情" + DateUtil.formatDate(new Date()) + ".csv";
        String tmpFilePath = cachePath + sftpFileName;
        ChannelSftp sftp = null;
        File tmpFile = null;
        try {
            // 生成CSV文件
            long start = System.currentTimeMillis();
            log.debug("uploadK12SummerData write start tmpFilePath is :{}", tmpFilePath);
            // 创建CSV写入器
            CsvWriter writer = CsvUtil.getWriter(tmpFilePath, CharsetUtil.CHARSET_UTF_8);
            // 获取映射类的字段信息
            Class<?> clazz = K12SchoolsProofreadCsvDTO.class;
            Field[] fields = ReflectUtil.getFields(clazz);
            // 写入表头
            String[] headers = new String[fields.length];
            for (int i = 0; i < fields.length; i++) {
                Alias alias = fields[i].getAnnotation(Alias.class);
                if (ObjectUtil.isNotNull(alias)) {
                    headers[i] = alias.value();
                } else {
                    headers[i] = fields[i].getName();
                }
            }
            writer.write(headers);
            // 查询数据文件
            getK12SchoolCsvData(writer, fields);
            // 关闭写入器
            writer.close();
            log.debug("---CSV文件生成完成,耗时:" + (System.currentTimeMillis() - start) + "毫秒----");

            tmpFile = new File(tmpFilePath);
            // 创建连接
            sftp = SftpUtils.createSftp(config);
            // 上传文件
            SftpUtils.upload(config.getFilePath(), sftpFileName, Files.newInputStream(tmpFile.toPath()), sftp);
        } catch (Exception e) {
            log.error("uploadK12SummerData exception, e:{}", e.getLocalizedMessage(), e);
        } finally {
            SftpUtils.disconnect(sftp);
            // 删除临时文件
            if (tmpFile != null && tmpFile.exists()) {
                boolean delete = tmpFile.delete();
            }
        }
        log.debug("K12SchoolsProofreadServiceImpl uploadK12SummerData end");
    }

    private void getK12SchoolCsvData(CsvWriter writer, Field[] fields) {
        log.debug("K12SchoolsProofreadServiceImpl getK12SchoolCsvData begin");
        int batchSize = 2000;
        int count = this.count(new QueryWrapper<K12SchoolsProofread>());
        int pages = (count / batchSize) + 1;
        ArrayList<K12SchoolsProofreadCsvDTO> resultList = new ArrayList<>();
        SyncTool.doSync(pages, i -> {
            Page<K12SchoolsProofreadExportTotalDTO> page = new Page<>(i, batchSize);
            List<K12SchoolsProofreadCsvDTO> proofreadCsvDataList = k12SchoolsProofreadMapper.getProofreadCsvData(page);
            synchronized (writer) {
                // 写入数据
                for (K12SchoolsProofreadCsvDTO data : proofreadCsvDataList) {
                    String[] row = new String[fields.length];
                    for (int j = 0; j < fields.length; j++) {
                        Object value = ReflectUtil.getFieldValue(data, fields[j]);
                        row[j] = ObjectUtil.isNull(value) ? StrUtil.EMPTY : value.toString();
                    }
                    writer.write(row);
                }
            }
        });
        log.debug("K12SchoolsProofreadServiceImpl getK12SchoolCsvData end");
    }

    private SftpConfig initSftpConfig() {
        log.debug("K12SchoolsProofreadServiceImpl initSftpConfig begin");
        SftpConfig config = new SftpConfig();
        String k12UploadCsvConfig = DictUtil.getDictItemValueByName(SftpUtils.SFTP_CONFIG, "K12UploadCsvConfig");
        String desc = DictUtil.getDescValueByName(SftpUtils.SFTP_CONFIG, "K12UploadCsvConfig");
        log.debug("K12SchoolsProofreadServiceImpl initSftpConfig k12UploadCsvConfig :{}", k12UploadCsvConfig);
        String[] dictConfig = k12UploadCsvConfig.split(":");
        if (dictConfig.length < 4) {
            log.debug("K12SchoolsProofreadServiceImpl initSftpConfig end config error");
            return null;
        }
        // 查询字典配置   设置sftp 参数
        config.setHost(dictConfig[0]);
        config.setPort(Integer.parseInt(dictConfig[1]));
        config.setUsername(dictConfig[2]);
        config.setPassword(dictConfig[3]);
        config.setFilePath(desc);
        log.debug("K12SchoolsProofreadServiceImpl initSftpConfig end");
        return config;
    }

    private Map<String, String> getDistrictIdNameMap(MultipartFile file) {
        log.debug("getDistrictIdNameMap begin");
        Map<String, String> resultMap = new HashMap<>();
        try {
            EasyExcelFactory.read(file.getInputStream(), DistinctInfo.class, new ReadListener<DistinctInfo>() {
                @Override
                public void onException(Exception exception, AnalysisContext context) {
                    if (exception instanceof ExcelDataConvertException) {
                        log.error("解析区县信息异常, 行：{}", ((ExcelDataConvertException) exception).getRowIndex());
                    } else {
                        log.error("解析区县信息异常, {}", exception.getClass().getName());
                    }
                }

                @Override
                public void invoke(DistinctInfo data, AnalysisContext context) {
                    resultMap.put(data.getId(), data.getName());
                }

                @Override
                public void doAfterAllAnalysed(AnalysisContext context) {
                }
            }).sheet().doReadSync();
        } catch (IOException e) {
            log.error("解析区县信息失败", e);
        }
        log.debug("getDistrictIdNameMap end resultMap is:{}", JSON.toJSONString(resultMap));
        return resultMap;
    }

}
